using System;
using System.Collections;
using System.Diagnostics;
using System.IO;

namespace TestUnits {

    public abstract class Project {
        public Project(string projectFile) {
            ProjectFile = projectFile;
            m_sourceFiles = new ArrayList();
            m_assemblyInfoFile = string.Empty;
            m_modified = false;
        }

        public void AddFile(string relativeFilename) {
            if (IsVersionFile(relativeFilename)) {
                Debug.Assert(m_assemblyInfoFile == string.Empty);
                m_assemblyInfoFile = relativeFilename;
            }
            else
                m_sourceFiles.Add(relativeFilename);
        }

        protected abstract bool IsVersionFile(string relativeFilename);

        public bool Modified {
            get {
                return m_modified;
            }
        }

        public void MarkProjectModified(bool modified) {
            if (modified) {
                Debug.Assert(m_sourceFiles.Count > 0);
                SetWriteTime((string)m_sourceFiles[0]);
            }
            else {
                Debug.Assert(m_assemblyInfoFile != string.Empty);
                SetWriteTime(m_assemblyInfoFile);
            }
            m_modified = modified;
        }

        public BuildAutoIncrement.AssemblyVersions GetAssemblyVersions() {
            BuildAutoIncrement.AssemblyInfoStream ais = new BuildAutoIncrement.AssemblyInfoStream(m_assemblyInfoFile);
            return ais.GetVersions();
        }

        protected void SetWriteTime(string relativeFilename) {
            string path = Path.Combine(Path.GetDirectoryName(ProjectFile), relativeFilename);
            File.SetLastWriteTime(path, DateTime.Now);
        }

        public readonly string ProjectFile;

        private bool m_modified;

        private string m_assemblyInfoFile;

        private ArrayList m_sourceFiles;
    }

    public class CSharpProject : Project {
        public CSharpProject(string projectFile) : base(projectFile) {
        }

        protected override bool IsVersionFile(string filename) {
            return string.Compare(Path.GetFileName(filename), "AssemblyInfo.cs", true) == 0;
        }
    }

	/// <summary>
	///   Helper class responsible to copy and clean-up test projects.
	/// </summary>
    public class SolutionTestHelper {

        public SolutionTestHelper(string solutionFilename) {
            Debug.Assert(File.Exists(solutionFilename));
            SolutionFilename = solutionFilename;
            m_projects = new Hashtable();
        }

        public string SolutionRoot {
            get { return Path.GetDirectoryName(SolutionFilename); }
        }

        public void CopySolution(string destinationFolder) {
            DestinationRoot = destinationFolder;
            string destination = Path.Combine(DestinationRoot, Path.GetFileName(SolutionFilename));
            File.Copy(SolutionFilename, destination, true);
            FileAttributes fa = File.GetAttributes(destination);
            if ((fa & FileAttributes.ReadOnly) == FileAttributes.ReadOnly) {
                fa ^= FileAttributes.ReadOnly;
                File.SetAttributes(destination, fa);
            }
            CopyDirRecusive(SolutionRoot, DestinationRoot, false);
        }

        public void AddProject(string projectFileName, Project project) {
            string key = Path.GetFileNameWithoutExtension(projectFileName);
            m_projects.Add(key, project);
        }

        public void DeleteDestination() {
            IEnumerator ienum = m_projects.Values.GetEnumerator();
            while (ienum.MoveNext()) {
                Project p = (Project)ienum.Current;
                string pojectDir = Path.GetDirectoryName(p.ProjectFile);
                Directory.Delete(pojectDir, true);
            }
            File.Delete(Path.Combine(DestinationRoot, Path.GetFileName(SolutionFilename)));
        }

        public void SetProjectModified(string projectFilename, bool modified) {
            string key = Path.GetFileNameWithoutExtension(projectFilename);
            ((Project)m_projects[key]).MarkProjectModified(modified);
        }

        public bool IsProjectMarkedForModification(string projectFilename) {
            string key = Path.GetFileNameWithoutExtension(projectFilename);
            return ((Project)m_projects[key]).Modified;
        }

        public string[] GetProjectNamesByModification(bool modified) {
            ArrayList al = new ArrayList();
            IDictionaryEnumerator ie = m_projects.GetEnumerator();
            while (ie.MoveNext()) {
                Project p = (Project)ie.Value;
                if (p.Modified == modified) {
                    al.Add(ie.Key);
                }
            }
            return (string[])al.ToArray(typeof(string));
        }

        private void CopyDirRecusive(string sourceDirectory, string destinationDirectory, bool copyFiles) {
            Debug.Assert(Path.IsPathRooted(destinationDirectory));
            Debug.Assert(Directory.Exists(sourceDirectory));
            // determine if the destination directory exists, if not create it
            if (!Directory.Exists(destinationDirectory)) {
                Directory.CreateDirectory(destinationDirectory);
            }

            DirectoryInfo directoryInfo = new DirectoryInfo(sourceDirectory);
            if (copyFiles) {
                // get all files in the current dir
                FileInfo[] files = directoryInfo.GetFiles();
                // loop through each file
                foreach (FileInfo file in files) {
                    if (string.Compare(file.Extension, ".user", true) != 0 && string.Compare(file.Extension, ".scc", true) != 0 
                        && string.Compare(file.Extension, ".vspscc", true) != 0 && string.Compare(file.Extension, ".suo", true) != 0) {
                        //create the path to where this file should be in destdir
                        string destination = Path.Combine(destinationDirectory, file.Name);                
                        //copy file to dest dir
                        file.CopyTo(destination, true);
                        // remove read-only attribute
                        FileAttributes fa = File.GetAttributes(destination);
                        if ((fa & FileAttributes.ReadOnly) == FileAttributes.ReadOnly) {
                            fa ^= FileAttributes.ReadOnly;
                            File.SetAttributes(destination, fa);
                        }
                    }
                }
            }
            // get dirs
            DirectoryInfo[] dirs = directoryInfo.GetDirectories();
            // loop through each sub directory in the current dir
            foreach (DirectoryInfo subdir in dirs) {
                if (string.Compare(subdir.Name, "obj", true) != 0 && string.Compare(subdir.Name, "bin", true) != 0) {
                    // create the path to the directory in destdir
                    string destination = Path.Combine(destinationDirectory, subdir.Name);
                    // recursively call this function over and over again
                    // with each new dir.
                    CopyDirRecusive(subdir.FullName, destination, true);
                }
            }
        }

        private readonly string SolutionFilename;
        private string DestinationRoot;

        private Hashtable m_projects;
    }
}